<?php
declare(strict_types=1);
require __DIR__ . '/config.php';

const SECTIONS = [
  'articles'   => ['База знаний', 'Статьи, разборы, методики'],
  'cases'      => ['Кейсы', 'Инженерные расследования'],
  'faults'     => ['Каталог неисправностей', 'Оборудование, симптомы, диагностика'],
  'templates'  => ['Шаблоны документов', 'Акты, ведомости, журналы'],
  'checklists' => ['Чек-листы', 'Проверки и формы для скачивания'],
];

// Запасные функции, если на хостинге нет расширения mbstring
if (!function_exists('mb_strlen')) { function mb_strlen(string $s): int { return (int)preg_match_all('/./us', $s); } }
if (!function_exists('mb_substr')) { function mb_substr(string $s, int $a, ?int $l = null): string { preg_match_all('/./us', $s, $m); return implode('', array_slice($m[0], $a, $l)); } }

const PER_PAGE = 12;
const VISIBLE = 'm.published=1 AND (m.publish_date IS NULL OR m.publish_date<=NOW())';

function e(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function db(): PDO {
  static $p = null;
  if (!$p) $p = new PDO(DB_DSN, DB_USER, DB_PASS, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => true,
  ]);
  return $p;
}
function q(string $sql, array $a = []): array { $s = db()->prepare($sql); $s->execute($a); return $s->fetchAll(); }
function dt(array $r): string { return (string)($r['publish_date'] ?: $r['created_at']); }
function cut(string $s, int $n): string { $s = trim(preg_replace('/\s+/u', ' ', strip_tags($s))); return mb_strlen($s) > $n ? mb_substr($s, 0, $n - 1) . '…' : $s; }

function layout(string $title, string $desc, string $body, string $path, array $o = []): void {
  $canon = SITE_URL . $path;
  $t = e($title . ' — ' . SITE_NAME);
  echo '<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">';
  echo "<title>$t</title><meta name=\"description\" content=\"" . e($desc) . "\"><link rel=\"canonical\" href=\"" . e($canon) . "\">";
  if (!empty($o['keywords'])) echo '<meta name="keywords" content="' . e($o['keywords']) . '">';
  if (!empty($o['noindex'])) echo '<meta name="robots" content="noindex,follow">';
  echo "<meta property=\"og:title\" content=\"$t\"><meta property=\"og:description\" content=\"" . e($desc) . '"><meta property="og:type" content="article">';
  if (!empty($o['image'])) echo '<meta property="og:image" content="' . e($o['image']) . '">';
  echo '<link rel="stylesheet" href="/assets/style.css">';
  if (!empty($o['ld'])) echo '<script type="application/ld+json">' . json_encode($o['ld'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG) . '</script>';
  echo '</head><body><header class="top"><div class="wrap"><a class="logo" href="/index.html"><i></i>' . e(SITE_NAME) . '</a><nav>';
  echo '<a href="/index.html#services">Услуги</a>';
  foreach (SECTIONS as $k => $v) echo '<a href="/' . $k . '/">' . e($v[0]) . '</a>';
  echo '<a href="/index.html#request">Заявка</a></nav></div></header><main class="wrap" style="padding-top:40px;padding-bottom:60px">' . $body . '</main>';
  echo '<footer><div class="wrap">' . e(SITE_NAME) . ' · Диагностика промышленного оборудования · <a href="/index.html">На главную</a></div></footer></body></html>';
}

function not_found(): void {
  http_response_code(404);
  layout('Страница не найдена', 'Страница не найдена', '<h1>Страница не найдена</h1><p class="lead">Возможно, материал ещё не опубликован или адрес изменился.</p><p><a class="btn" href="/index.html">На главную</a></p>', '/404', ['noindex' => 1]);
}

function list_page(string $sec): void {
  [$name, $sub] = SECTIONS[$sec];
  $cat = (string)($_GET['cat'] ?? ''); $qs = trim((string)($_GET['q'] ?? '')); $pg = max(1, (int)($_GET['p'] ?? 1));
  $w = ['m.section=:s', VISIBLE]; $a = ['s' => $sec];
  if ($cat !== '') { $w[] = 'c.slug=:c'; $a['c'] = $cat; }
  if ($qs !== '') { $w[] = '(m.title LIKE :q OR m.excerpt LIKE :q OR m.meta_keywords LIKE :q OR m.brand LIKE :q OR m.series LIKE :q)'; $a['q'] = '%' . $qs . '%'; }
  $from = 'FROM materials m LEFT JOIN categories c ON c.id=m.category_id WHERE ' . implode(' AND ', $w);
  $total = (int)q("SELECT COUNT(*) n $from", $a)[0]['n'];
  $rows = q("SELECT m.title,m.slug,m.excerpt,m.brand,m.series,m.file_format,m.is_training,c.name cat $from ORDER BY COALESCE(m.publish_date,m.created_at) DESC,m.id DESC LIMIT " . PER_PAGE . ' OFFSET ' . (($pg - 1) * PER_PAGE), $a);
  $cats = q('SELECT slug,name FROM categories WHERE section=:s ORDER BY sort,name', ['s' => $sec]);
  $b = '<p class="crumbs"><a href="/index.html">Главная</a> / ' . e($name) . '</p><h1>' . e($name) . '</h1><p class="lead">' . e($sub) . '</p>';
  $b .= '<form class="filter" method="get"><label>Поиск<input name="q" value="' . e($qs) . '" placeholder="Марка, серия, симптом"></label>';
  $b .= '<label>Категория<select name="cat"><option value="">Все</option>';
  foreach ($cats as $c) $b .= '<option value="' . e($c['slug']) . '"' . ($c['slug'] === $cat ? ' selected' : '') . '>' . e($c['name']) . '</option>';
  $b .= '</select></label><button class="btn" type="submit">Найти</button></form>';
  if (!$rows) $b .= '<p class="note">Пока ничего не опубликовано.</p>';
  $b .= '<div class="grid">';
  foreach ($rows as $r) {
    $k = array_filter([$r['cat'], trim($r['brand'] . ' ' . $r['series']), $r['file_format'] ? strtoupper($r['file_format']) : '', $r['is_training'] ? 'учебный сценарий' : '']);
    $b .= '<article class="card item"><p class="k">' . e(implode(' · ', $k)) . '</p><h3><a href="/' . $sec . '/' . e($r['slug']) . '">' . e($r['title']) . '</a></h3><p>' . e(cut((string)$r['excerpt'], 200)) . '</p></article>';
  }
  $b .= '</div>';
  $pages = (int)ceil($total / PER_PAGE);
  if ($pages > 1) {
    $b .= '<div class="pager">';
    for ($i = 1; $i <= $pages; $i++) $b .= $i === $pg ? "<b>$i</b>" : '<a href="?' . e(http_build_query(array_filter(['q' => $qs, 'cat' => $cat, 'p' => $i > 1 ? $i : null]))) . "\">$i</a>";
    $b .= '</div>';
  }
  layout($name, $sub, $b, '/' . $sec . '/', ['noindex' => ($qs !== '' || $cat !== '' || $pg > 1)]);
}

function detail_page(string $sec, string $slug): void {
  $r = q('SELECT m.*,c.name cat,a.name author FROM materials m LEFT JOIN categories c ON c.id=m.category_id LEFT JOIN authors a ON a.id=m.author_id WHERE m.section=:s AND m.slug=:l AND ' . VISIBLE, ['s' => $sec, 'l' => $slug]);
  if (!$r) { not_found(); return; }
  $r = $r[0]; $name = SECTIONS[$sec][0]; $path = '/' . $sec . '/' . $slug;
  $desc = $r['meta_description'] ?: cut((string)$r['excerpt'], 160);
  $b = '<p class="crumbs"><a href="/index.html">Главная</a> / <a href="/' . $sec . '/">' . e($name) . '</a></p>';
  $b .= '<p class="eyebrow">' . e(implode(' · ', array_filter([$r['cat'], $r['is_training'] ? 'учебный сценарий' : '', trim($r['brand'] . ' ' . $r['series'])]))) . '</p>';
  $b .= '<h1>' . e($r['title']) . '</h1>';
  if ($r['excerpt']) $b .= '<p class="lead">' . e($r['excerpt']) . '</p>';
  $b .= '<div class="meta"><span>' . e(substr(dt($r), 0, 10)) . '</span>' . ($r['author'] ? '<span>' . e($r['author']) . '</span>' : '') . '</div>';
  if ($r['is_training']) $b .= '<div class="notice">Это учебный сценарий, а не выполненный проект. Клиенты и данные вымышлены.</div>';
  if ($r['file_url']) $b .= '<p><a class="btn" href="' . e($r['file_url']) . '" download>Скачать' . ($r['file_format'] ? ' ' . e(strtoupper($r['file_format'])) : '') . '</a></p>';
  $b .= '<div class="article">' . $r['content'] . '</div>'; // HTML вставляете вы сами, наружу формы нет
  $b .= '<section class="card" style="margin:34px 0 0"><h2 style="font-size:24px">Нужна диагностика или документы?</h2><p>Опишите неисправность. Обязательных полей нет.</p><a class="btn" href="/index.html#request">Описать неисправность</a></section>';
  $rel = q('SELECT m.title,m.slug FROM materials m WHERE m.section=:s AND m.slug<>:l AND ' . VISIBLE . ' ORDER BY COALESCE(m.publish_date,m.created_at) DESC LIMIT 3', ['s' => $sec, 'l' => $slug]);
  if ($rel) { $b .= '<h2 style="font-size:24px;margin-top:40px">Ещё в разделе</h2><ul class="plain">'; foreach ($rel as $x) $b .= '<li><a href="/' . $sec . '/' . e($x['slug']) . '">' . e($x['title']) . '</a></li>'; $b .= '</ul>'; }
  $ld = ['@context' => 'https://schema.org', '@type' => 'Article', 'headline' => $r['title'], 'description' => $desc, 'datePublished' => dt($r), 'dateModified' => (string)$r['updated_at'],
    'mainEntityOfPage' => SITE_URL . $path, 'publisher' => ['@type' => 'Organization', 'name' => SITE_NAME]];
  if ($r['author']) $ld['author'] = ['@type' => 'Person', 'name' => $r['author']];
  if ($r['image_url']) $ld['image'] = $r['image_url'];
  layout($r['title'], $desc, $b, $path, ['keywords' => $r['meta_keywords'], 'image' => $r['image_url'], 'ld' => $ld]);
}

function sitemap(): void {
  header('Content-Type: application/xml; charset=utf-8');
  echo '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
  echo '<url><loc>' . e(SITE_URL . '/') . '</loc></url>';
  foreach (SECTIONS as $k => $v) echo '<url><loc>' . e(SITE_URL . "/$k/") . '</loc></url>';
  foreach (q('SELECT m.section,m.slug,m.updated_at FROM materials m WHERE ' . VISIBLE . ' ORDER BY m.id') as $r)
    echo '<url><loc>' . e(SITE_URL . '/' . $r['section'] . '/' . $r['slug']) . '</loc><lastmod>' . e(substr((string)$r['updated_at'], 0, 10)) . '</lastmod></url>';
  echo '</urlset>';
}

function latest(): void {
  header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: public, max-age=300');
  $out = [];
  foreach (q('SELECT m.section,m.title,m.slug,m.excerpt FROM materials m WHERE ' . VISIBLE . ' ORDER BY COALESCE(m.publish_date,m.created_at) DESC,m.id DESC LIMIT 6') as $r)
    $out[] = ['section' => SECTIONS[$r['section']][0] ?? '', 'title' => $r['title'], 'url' => '/' . $r['section'] . '/' . $r['slug'], 'excerpt' => cut((string)$r['excerpt'], 140)];
  echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

$path = trim((string)parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/');
$p = $path === '' ? [] : explode('/', $path);
try {
  if ($path === 'sitemap.xml') sitemap();
  elseif ($path === 'api/latest.json') latest();
  elseif (isset($p[0], SECTIONS[$p[0]]) && count($p) === 1) list_page($p[0]);
  elseif (isset($p[0], SECTIONS[$p[0]]) && count($p) === 2 && preg_match('/^[a-z0-9-]{1,190}$/', $p[1])) detail_page($p[0], $p[1]);
  else not_found();
} catch (Throwable $ex) {
  error_log((string)$ex);
  http_response_code(500);
  layout('Ошибка', 'Временная ошибка', '<h1>Временная ошибка</h1><p class="lead">Попробуйте обновить страницу позже.</p>', '/error', ['noindex' => 1]);
}
